import json
import time
import struct
import random
import socks
from paho.mqtt import client as mqtt_client

# 测试，使用的是 cabinet 模板的用户名密码
broker = 'afubzfb.iot.gz.baidubce.com'
port = 1883
client_id = f'python-mqtt-{random.randint(0, 1000)}'
username = "thingidp@afubzfb|test_server|0|MD5"
password = "ca0529335a768bb2c7d0fb6affdd2aa4"


def bytes_to_float(h1, h2, h3, h4):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    ba.append(h3)
    ba.append(h4)
    return struct.unpack("!f", ba)[0]


def float_to_bytes(f):
    bs = struct.pack("f", f)
    return bs[3], bs[2], bs[1], bs[0]


def bytes_to_short(h1, h2):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    return struct.unpack('!h', ba)[0]


def short_to_bytes(h):
    bs = struct.pack('h', h)
    return bs[1], bs[0]


def bytes_to_u_short(h1, h2):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    return struct.unpack('!H', ba)[0]


def u_short_to_bytes(h):
    bs = struct.pack('H', h)
    return bs[1], bs[0]


def bytes_to_str(bytesarr):
    if bytesarr == None:
        return
    new_arr = bytearray()
    for b in bytesarr:
        if b != 0x00:
            new_arr.append(b)
    return new_arr.decode('utf-8')


def str_to_bytes(s, target_bytes_len):
    s = bytes(s.encode('utf-8'))
    s = bytearray(s)
    if len(s) > target_bytes_len:
        print('[ERR] string is too long for target_bytes_len')
        return
    elif len(s) == target_bytes_len:
        return bytes(s)
    else:
        for i in range(target_bytes_len - len(s)):
            s.append(0x00)
        return bytes(s)


def print_hex(bytes):
    l = [hex(int(i)) for i in bytes]
    print(" ".join(l))


def connect_mqtt():
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            print("Connected to MQTT Broker!")
        else:
            print("Failed to connect, return code %d\n", rc)

    # Set Connecting Client ID
    client = mqtt_client.Client(client_id)
    client.username_pw_set(username, password)
    # 按需设置代理，因为测试时发现如果不指定走代理mqtt连接就会很慢，如果电脑上没有代理，那就注释掉下面那一行
    client.proxy_set(proxy_type=socks.SOCKS5, proxy_addr='127.0.0.1', proxy_port=7890)
    client.will_set(topic="cabinet/offline", payload=r'emulator, offline', qos=0, retain=True)
    client.on_connect = on_connect
    client.connect(broker, port)
    return client


def publish_test(client, topic):
    msg_count = 0
    while True:
        time.sleep(1)
        msg = f"messages: {msg_count}"
        result = client.publish(topic, msg)
        # result: [0, 1]
        status = result[0]
        if status == 0:
            print(f"Send `{msg}` to topic `{topic}`")
        else:
            print(f"Failed to send message to topic {topic}")
        msg_count += 1


def subscribe(client: mqtt_client, topic, on_message):
    client.subscribe(topic)
    client.on_message = on_message


def on_message_received(client, userdata, msg):
    # print(msg.payload[0])
    if msg.payload[0] == 123:
        print(json.loads(msg.payload))
        print(f"Received from `{msg.topic}` topic")
    else:
        decode_bytes(msg.payload)


def decode_bytes(payload):
    battery_num = int((len(payload) - 26) / 38)
    # print(battery_num)
    print('--电池柜部分--')
    print('电池柜编号', bytes_to_str(payload[0:12]))
    print('水位报警', bytes_to_u_short(payload[12], payload[13]))
    print('火灾报警', bytes_to_u_short(payload[14], payload[15]))
    print('GSM信号强度', bytes_to_short(payload[16], payload[17]))
    print('电池柜所在的经度', bytes_to_float(
        payload[18], payload[19], payload[20], payload[21]))
    print('电池柜所在的纬度', bytes_to_float(
        payload[22], payload[23], payload[24], payload[25]))
    # 电池部分
    print('--电池部分--')
    for i in range(battery_num):
        print('--电池--')
        offset = 26 + 38 * i
        # 电池的充电状态
        print('电池的充电状态', bytes_to_u_short(
            payload[offset + 0], payload[offset + 1]))
        # 电池的充电电压
        print('电池的充电电压', bytes_to_u_short(
            payload[offset + 2], payload[offset + 3]))
        # 电池的充电电流 s
        print('电池的充电电流', bytes_to_short(
            payload[offset + 4], payload[offset + 5]))
        # 电池的充电机状态
        print('电池的充电机状态', bytes_to_u_short(
            payload[offset + 6], payload[offset + 7]))
        # 电池编号
        print('电池编号', bytes_to_str(payload[offset + 8:offset + 16]))
        # 柜门内电池类型
        print('柜门内电池类型', bytes_to_u_short(
            payload[offset + 16], payload[offset + 17]))
        # 柜门状态
        print('柜门状态', bytes_to_u_short(
            payload[offset + 18], payload[offset + 19]))
        # 柜仓温度 s
        print('柜仓温度', bytes_to_short(
            payload[offset + 20], payload[offset + 21]))
        # 电池剩余电量所占的百分比
        print('电池剩余电量所占的百分比', bytes_to_u_short(
            payload[offset + 22], payload[offset + 23]))
        # 电池的剩余容量
        print('电池的剩余容量', bytes_to_u_short(
            payload[offset + 24], payload[offset + 25]))
        # 电池的电压
        print('电池的电压', bytes_to_u_short(
            payload[offset + 26], payload[offset + 27]))
        # 电池的电芯温度 s
        print('电池的电芯温度', bytes_to_short(
            payload[offset + 28], payload[offset + 29]))
        # 电池所在的经度 f
        print('电池所在的经度',
              bytes_to_float(payload[offset + 30], payload[offset + 31], payload[offset + 32], payload[offset + 33]))
        # 电池所在的纬度 f
        print('电池所在的纬度',
              bytes_to_float(payload[offset + 34], payload[offset + 35], payload[offset + 36], payload[offset + 37]))


if __name__ == '__main__':
    client = connect_mqtt()
    # client.loop_start()
    time.sleep(1)
    subscribe(client, 'cabinet/state', on_message_received)

    client.loop_forever()
    # client.disconnect()
